package cn.ibizlab.core.authentication.service.impl;

import cn.ibizlab.core.ad.domain.SysPerson;
import cn.ibizlab.core.authentication.constants.AuthenticationConstant;
import cn.ibizlab.core.authentication.domain.AuthLogin;
import cn.ibizlab.core.authentication.domain.AuthProvider;
import cn.ibizlab.core.authentication.domain.ExecutionResult;
import cn.ibizlab.core.authentication.provider.AuthenticationService;
import cn.ibizlab.util.common.ConfigParser;
import cn.ibizlab.util.enums.Entities;
import cn.ibizlab.util.errors.BadRequestAlertException;
import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;

@Data
@Slf4j
public class ServiceApiAuthenticationService implements AuthenticationService {

    String id;
    RestTemplate restTemplate;
    AuthProvider provider;
    /**
     * 根据provider构造LdapAuthenticationService
     * @param provider
     */
    public ServiceApiAuthenticationService(AuthProvider provider){
        RestTemplate restTemplate = new RestTemplate();
        this.restTemplate = restTemplate;
        this.provider = provider;
        this.id = DigestUtils.md5DigestAsHex(JSON.toJSONString(provider).getBytes());
    }

    /**
     * 身份认证
     * @param authentication 用户身份信息
     * @return
     */
    @Override
    public Authentication authenticate(Authentication authentication){

        String userName = !ObjectUtils.isEmpty(authentication.getPrincipal())? authentication.getPrincipal().toString() : null;
        String password = !ObjectUtils.isEmpty(authentication.getCredentials())? authentication.getCredentials().toString() : null;
        log.debug(String.format("正在使用[%1$s]认证服务对[%2$s]用户登录进行验证",provider.getName(),userName));
        Long startTime = System.currentTimeMillis();

        String loginName="";
        String domains="";
        String[] usernameArrays = userName.split("[|]");
        if(usernameArrays.length>0)
            loginName = usernameArrays[0].trim();
        if(usernameArrays.length>1)
            domains = usernameArrays[1].trim();

        AuthLogin authLogin = new AuthLogin();
        authLogin.setLoginName(loginName);
        authLogin.setPassword(password);
        if(!ObjectUtils.isEmpty(domains))
            authLogin.setDc(domains);

        //登录属性映射列表
        Map<String,Object> requestParamMappings = extractParams2Map(provider.getBaseConfig());
        //响应结果属性映射列表
        Map<String,Object> responseParamMappings = extractParams2Map(provider.getResponseConfig());
        //按照应用sso配置构造请求体
        Map requestBody = new HashMap();
        if(!ObjectUtils.isEmpty(requestParamMappings)){
            for(Map.Entry<String,Object> entry : requestParamMappings.entrySet()){
                requestBody.put(entry.getKey(),authLogin.get((String)entry.getValue()));
            }
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        try {
            //调用登录认证接口
            ResponseEntity<ExecutionResult> response = restTemplate.exchange(provider.getAuthenticationUri(), HttpMethod.valueOf(provider.getAuthenticationMethod()) , new HttpEntity(requestBody,headers), ExecutionResult.class);
            boolean isSuccess = checkResponse(provider,response);
            if(!isSuccess){
                log.error(String.format("使用[%1$s]认证服务验证[%2$s]用户登录出现异常，异常信息：[%3$s] , 耗时[%4$s]ms", provider.getName() , userName, JSON.toJSONString(response.getBody()) , System.currentTimeMillis()-startTime));
                return null;
            }

            log.debug(String.format("使用[%1$s]认证服务验证[%2$s]用户登录成功，响应内容：[%3$s] , 耗时[%4$s]ms", provider.getName(), userName ,JSON.toJSONString(response.getBody()), System.currentTimeMillis()- startTime));

            //认证成功，根据接口响应用户信息构造person
            Map resultParams = response.getBody().getParams();
            SysPerson person = new SysPerson();
            if(!ObjectUtils.isEmpty(responseParamMappings)){
                for(Map.Entry<String,Object> entry : responseParamMappings.entrySet()){
                    person.set(entry.getKey(),resultParams.get(entry.getValue()));
                }
            }

            UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(userName,
                    password,null);
            result.setDetails(person);
            return result;

        }catch (RuntimeException e){
            log.error(String.format("使用[%1$s]认证服务验证[%2$s]用户登录出现异常，异常信息：[%3$s] , 耗时[%4$s]ms", provider.getName() , userName, e.getMessage() ,System.currentTimeMillis()- startTime));
            return null;
        }
    }

    /**
     * 解析响应结果，返回接口认证结果（成功/失败)
     * @param provider
     * @param response
     * @return
     */
    protected boolean checkResponse(AuthProvider provider , ResponseEntity<ExecutionResult> response) {

        ExecutionResult result = response.getBody();
        if(result == null || result.getParams() == null)
            throw new BadRequestAlertException("解析用户身份失败，接口返回参数不足", Entities.AUTH_LOGIN.toString(),"serviceApiLogin");

        boolean isSuccess = false;
        String responseType = provider.getResponseType();
        String successFlagParams = provider.getResponseSuccessFlag();
        //接口响应标记，http状态码与自定义状态码，自定义状态码需读取配置判断响应结果成功/失败
        if(!ObjectUtils.isEmpty(responseType) && "custom".equalsIgnoreCase(responseType)){

            if(ObjectUtils.isEmpty(successFlagParams))
                throw new BadRequestAlertException("未配置响应成功标记", Entities.AUTH_LOGIN.toString(),"serviceApiLogin");

            Map<String,Object> successFlagMap  = extractParams2Map(successFlagParams);
            Map params = response.getBody().getParams();
            if(ObjectUtils.isEmpty(params) || ObjectUtils.isEmpty(successFlagMap)){
                return false;
            }
            //响应的code与配置成功标记进行比较
            for(Map.Entry<String,Object> entry : successFlagMap.entrySet()){
                 Object responseCode = params.get(entry.getKey());
                 if(!ObjectUtils.isEmpty(responseCode) && responseCode.equals(entry.getValue())){
                    return true;
                 }
            }
        }
        else{
            isSuccess = (HttpStatus.OK == response.getStatusCode());
        }
        return isSuccess;
    }

    /**
     * 参数转换：key = value 转 map
     * @param params
     * @return
     */
    protected Map<String, Object> extractParams2Map(String params) {
        if(ObjectUtils.isEmpty(params))
            return null;

        return ConfigParser.parseConfig(params);
    }

    /**
     * 获取服务类型
     * @return
     */
    public String getServiceType(){
        if(provider != null){
            return provider.getName();
        }
        return AuthenticationConstant.PROVIDER_API;
    }
}
